/*
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.spice.persistenence.service;

import static org.spice.core.validation.Assert.ifItFails;
import static org.spice.core.validation.Assert.ifObjectFails;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collection;
import java.util.List;

import org.spice.persistence.components.Delete;
import org.spice.persistence.components.Insert;
import org.spice.persistence.components.Mapper;
import org.spice.persistence.components.Select;
import org.spice.persistence.components.Update;
import org.spice.persistence.exception.NoDataFoundException;
import org.spice.persistence.manager.RowMapper;
import org.spice.persistence.statement.PreparedStatementCallBack;

public 
class SpiceTemplate extends ServiceLookup implements Insert, Update, Select, Delete{


	public 
	SpiceTemplate(final String jndiName) {
		
	}

	
	@Override
	public 
	<T> void delete(T value) {
		ifItFails(value, "Value cannot be empty");
	}

	@Override
	public 
	<T> void deleteAll(final List<T> value) {
		ifItFails(value, "Value cannot be empty");
	}

	@Override
	public 
	<T> void delete(String query, Object... objects) throws NoDataFoundException {
		ifItFails(query, "Query cannot be empty");
		ifObjectFails(objects, "Object Arguments cannot be empty");
		
		executeDelete(query, objects, preparedStatementCallBack());
		
	}

	/**
	 * 
	 * @param query
	 * @param objects
	 * @param preparedStatementCallBack
	 * @throws NoDataFoundException 
	 */
	private 
	void executeDelete(String query, Object[] objects, PreparedStatementCallBack preparedStatementCallBack) throws NoDataFoundException {
		preparedStatementCallBack.doInStatement(query, objects, getConnection(), ServiceOperation.DELETE);
		
	}


	@Override
	public 
	<T> void delete(String query) {
		
	}

	@Override
	public 
	<T> T select(String query) {
		return null;
	}

	@Override
	public
	<T> T select(String query, Object... arguments) {
		return null;
	}

	@Override
	public 
	<T> T select(Class<T> value, Object... arguments) {
		return null;
	}

	@Override
	public 
	<T> T select(Class<T> value, Long id) {
		return null;
	}

	@Override
	public 
	<T> List<T> select(String query, Mapper<T> mapper, Object... objects) throws NoDataFoundException {
		ifItFails(query, "The query cannot be empty !");
		
		final List<T> result = execute(query, mapper, preparedStatementCallBack(), objects);
		return  result;
	}


	/**
	 * Creates an execution procedure for the prepared statements.
	 * 
	 * @return
	 */
	protected 
	PreparedStatementCallBack preparedStatementCallBack() {
		return new PreparedStatementCallBack() {
			
			@SuppressWarnings("unchecked")
			@Override
			public 
			<P> P doInStatement(final String query, final Object[] params, final Connection connection, final ServiceOperation serviceOperation) throws NoDataFoundException {
				ResultSet resultSet = null;
				Integer update = null;
				PreparedStatement statement = null;
				try {
					statement = connection.prepareStatement(query);
					objectParameterization(params, statement);
					if(serviceOperation.equals(ServiceOperation.SELECT)){
						resultSet = statement.executeQuery();
					}
					else{
						update = statement.executeUpdate();
						return (P) update;
					}
					if(null == resultSet)
						throw new NoDataFoundException("No data to be found");
					
				
				} catch (SQLException e) {
					throw new NoDataFoundException(e.getMessage());
				}finally{
					clearStatement(statement);
				}
				return (P) resultSet;
			}
			/**
			 * 
			 * @param params
			 * @param statement
			 * @throws SQLException
			 */
			protected 
			void objectParameterization(final Object[] params, PreparedStatement statement) throws SQLException {
				for (int i = 0; i < params.length; i++) {
				    if (params[i] != null) 
				    	statement.setObject(i + 1, params[i]);
				    else 
				    	statement.setNull(i + 1, Types.OTHER);
				    
				}
			}
			/**
			 * 	
			 * @param statement
			 */
			private 
			void clearStatement(PreparedStatement statement) {
				if(null != statement)
					try {
						statement.close();
					} catch (SQLException e) {
						// Statement cannot be closed, can be ignored.
					}
			}
			
		
		};
	}
	
	/**
	 * 
	 * @param <T>
	 * @param expressionQuery
	 * @param mapper
	 * @param callback
	 * @return
	 * @throws NoDataFoundException 
	 */
	protected
	<T> List<T> execute(final String expressionQuery, final Mapper<T> mapper, final PreparedStatementCallBack callback, final Object... objects) throws NoDataFoundException{
		
		final ResultSet resultSet = callback.doInStatement(expressionQuery, objects, getConnection(), ServiceOperation.SELECT);
		final RowMapper<T> rowMapper = new RowMapper<T>(mapper);
		final List<T> result = rowMapper.getObject(resultSet);
		clearConnection(getConnection(), resultSet);
		return result;
		
	}

	

	@Override
	public 
	<T> void update(T value) {
		
	}

	@Override
	public 
	<T> void updateAll(Collection<?> collection) {
		
		
	}

	@Override
	public 
	<T> void update(String query, Object... objects) {
		
	}

	@Override
	public 
	<T> void update(String query) {
		
	}

	@Override
	public 
	<T> void insert(T value) {
		
	}

	@Override
	public 
	<T> void insertAll(Collection<?> collection) {
		
	}

	@Override
	public 
	<T> void insert(String query) {
		
	}


	@Override
	public <T> void insert(String query, Mapper<?> mapper, Object... arguments) {
		
	}


	
}
